package engine;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import ui.GraphicCircle;
import ui.GraphicLine;
import ui.Interface;

public class Boid extends GraphicCircle {

    private Interface gui;
    private Integer detectionPerimeter;
    private boolean drawDetectionPerimeter;
    private boolean drawSpeedVector;
    private boolean drawDesiredDirection;
    private Vector2D desiredDirection;
    private Double maxRotationAngle;
    
    public Boid(Vector2D position, Vector2D speed, Integer rayon, Color color, Color borderColor, Interface gui) {
        super(position, speed, rayon, color, borderColor);
        this.gui = gui;
        desiredDirection = new Vector2D(0., 0., 0.);
    }

    public void setDesiredDirection(Vector2D desiredDirection) {
        this.desiredDirection = desiredDirection;
    }

    public ArrayList<Boid> closeBoids(ArrayList<Boid> boids) {
        ArrayList<Boid> closeBoids = new ArrayList<Boid>();

        synchronized (boids) {
            for (Boid boid : boids) {
                //Si le boid est dans le perimetre et qu'il a une vitesse 
                if (boid.id != id && boid.getSpeed() != null && isClose(boid.position.getX(), boid.position.getY())) {
                    closeBoids.add(boid);
                }
            }
        }

        return closeBoids;
    }

    public Vector2D repulsion(ArrayList<Boid> boids) {
        Vector2D average = new Vector2D(0., 0., 0.);
        if (boids.size() == 0) {
            return average;
        }

        Vector2D boidToMe;
        for (Boid boid : boids) {
            boidToMe = Vector2D.sub(position, boid.position);
            average = Vector2D.add(average, boidToMe);
        }

        average = Vector2D.div(average, (double) boids.size());
        return average.normalize().clone();
    }

    public Vector2D aggregation(ArrayList<Boid> boids) {
        Vector2D average = new Vector2D(0., 0., 1.);
        if (boids.size() == 0) {
            return average;
        }

        Vector2D meToBoid;
        for (Boid boid : boids) {
            meToBoid = Vector2D.sub(boid.position, position);
            average = Vector2D.add(average, meToBoid);
        }

        average = Vector2D.div(average, (double) boids.size());
        return average.normalize().clone();
    }

    //S'aligne comme la moyenne des boids dans le perimetre
    public Vector2D align(ArrayList<Boid> boids) {
        Vector2D average = new Vector2D(0., 0., 0.);
        if (boids.size() == 0) {
            return average;
        }

        for (Boid boid : boids) {
            average = Vector2D.add(average, boid.getSpeed());
        }

        average = Vector2D.div(average, (double) boids.size());
        return average.normalize().clone();
    }

    public boolean isClose(Double x, Double y) {
        return (x - position.getX()) * (x - position.getX()) + (y - position.getY()) * (y - position.getY()) < detectionPerimeter * detectionPerimeter;
    }

    public void rotate(Double angle) {
        Vector2D newSpeed = new Vector2D(0.);
        Double x = getSpeed().getX();
        Double y = getSpeed().getY();

        newSpeed.setX(x * Math.cos(angle) + y * Math.sin(angle));
        newSpeed.setY(-x * Math.sin(angle) + y * Math.cos(angle));

        this.speed = newSpeed.clone();
    }

    public void rotateToDesiredDirection() {
        if (getSpeed() == null || desiredDirection == null) {
            return;
        }

        Integer rotationDirection;
        //Rotation vers la direction desiree
        Double angle = Vector2D.angleBetween(getSpeed(), desiredDirection);

        rotationDirection = (angle >= 0) ? 1 : -1;

        maxRotationAngle = (gui != null) ? gui.getRotationSpeed()/100. : 5/100.;
        
        if (maxRotationAngle <= Math.abs(angle)) {
            rotate(rotationDirection * maxRotationAngle);
        }
    }

    @Override
    public void draw(Graphics g) {
        //rayon de l'ui
        super.rayon = (gui != null) ? gui.getBoidRayon() : 5;
        
        Integer topLeftX, topLeftY, diameter;
        topLeftX = (int) (super.position.getX() - super.rayon);
        topLeftY = (int) (super.position.getY() - super.rayon);
        diameter = 2 * super.rayon;

        //Intérieur
        g.setColor(super.color);
        g.fillOval(topLeftX, topLeftY, diameter, diameter);

        //Bordure
        g.setColor(super.borderColor);
        g.drawOval(topLeftX, topLeftY, diameter, diameter);

        //Perimètre de détection
        drawDetectionPerimeter = (gui != null) ? gui.drawDetectionPerimeter() : false;
        detectionPerimeter = (gui != null) ? gui.getDetectionPerimeter() : 50;
        if (drawDetectionPerimeter) {
            Integer perimeterTopLeftX, perimeterTopLeftY, perimeterDiameter;
            perimeterTopLeftX = (int) (super.position.getX() - detectionPerimeter);
            perimeterTopLeftY = (int) (super.position.getY() - detectionPerimeter);
            perimeterDiameter = (int) (2 * detectionPerimeter);

            g.setColor(Color.YELLOW);
            g.drawOval(perimeterTopLeftX, perimeterTopLeftY, perimeterDiameter, perimeterDiameter);
        }

        //Vecteur vitesse desiré
        drawDesiredDirection = (gui != null) ? gui.drawDirection() : false;
        if (drawDesiredDirection && desiredDirection != null) {
            Integer speedP2X, speedP2Y;
            speedP2X = (int) (position.getX() + desiredDirection.getX() * GraphicLine.speedUnitToPixelRatio);
            speedP2Y = (int) (position.getY() + desiredDirection.getY() * GraphicLine.speedUnitToPixelRatio);

            g.setColor(Color.RED);
            g.drawLine(position.getX().intValue(), position.getY().intValue(), speedP2X, speedP2Y);
        }

        //Vecteur vitesse
        drawSpeedVector = (gui != null) ? gui.getDrawSpeed() : false;
        if (drawSpeedVector && getSpeed() != null) {
            Integer speedP2X, speedP2Y;
            speedP2X = (int) (position.getX() + getSpeed().getX() * GraphicLine.speedUnitToPixelRatio);
            speedP2Y = (int) (position.getY() + getSpeed().getY() * GraphicLine.speedUnitToPixelRatio);

            g.setColor(Color.BLACK);
            g.drawLine(position.getX().intValue(), position.getY().intValue(), speedP2X, speedP2Y);
        }
    }
}
